// SPDX-FileCopyrightText: 2023-2025 Sangfor Technologies Inc.
// SPDX-License-Identifier: Mulan PSL v2
package handler

import (
	"time"

	"code.geniusai.org/notebook-manager/pkg/config"
	"github.com/gin-gonic/gin/binding"
	"github.com/pborman/uuid"

	"github.com/gin-gonic/gin"

	"code.geniusai.org/notebook-manager/pkg/entity"
	"code.geniusai.org/notebook-manager/pkg/handler/validate"
	"code.geniusai.org/notebook-manager/pkg/log"
	"code.geniusai.org/notebook-manager/pkg/resource"
)

// notebook管理的处理结构体
type NotebookHandler struct {
	resourceManager *resource.Manager // 资源的管理器
	confData        *config.Conf      // 配置文件信息
	activeIDChan    chan string       // 活跃的notebookID会放到这个队列中
	activeIDSet     map[string]bool   // 缓存所有活跃的notebooID，map能够去重
}

func NewNotebookHandler(resourceManager *resource.Manager, confData *config.Conf) *NotebookHandler {
	ret := &NotebookHandler{resourceManager: resourceManager, confData: confData}
	// 当前语句会对notebook进行监听，判断是否在有效活跃期内
	ret.initActivator(resource.ActiveKeyCacheSize, resource.UpdateActiveInterval)
	return ret
}

// 定时更新获取的notebook；定时停止不活跃的notebook
func (n *NotebookHandler) initActivator(cacheSize int, intervalSecond int64) {
	n.activeIDChan = make(chan string, cacheSize)
	n.activeIDSet = make(map[string]bool, 0)
	updateTimer := time.NewTicker(time.Second * time.Duration(intervalSecond))

	// 定时更新
	go func() {
		var orgID string
		for {
			select {
			case orgID = <-n.activeIDChan:
				n.activeIDSet[orgID] = true
			case <-updateTimer.C:
				// get all keys; update; clear
				log.Infof("updating notebook active time, number of active notebook is: %v, number of ID queue is: %v",
					len(n.activeIDSet), len(n.activeIDChan))
				for activeID, _ := range n.activeIDSet {
					_ = n.resourceManager.ActiveNotebook(activeID)
				}
				n.activeIDSet = make(map[string]bool, 0)
			}
		}
	}()
	// check and stop inactive notebook
	go func() {
		checkTimer := time.NewTicker(time.Second * resource.StopInactiveInterval)
		for {
			<-checkTimer.C
			log.Infof("deleting inactive notebook")
			_ = n.resourceManager.StopInactiveNotebook(resource.StopInactiveExpireTime)
		}
	}()
}

// 创建notebook接口
// @Summary 创建notebook
// @Description 创建notebook
// @Tags code studio管理
// @Accept  json
// @Produce  json
// @Param userID path    string true "default"
// @Param Notebook body entity.Notebook true "test"
// @Success 200 {object} entity.HttpRespond{data=entity.Notebook} "按时间倒序排序"
// @Failure 200 {object} entity.HttpRespond
// @Router /users/{userID}/notebooks [post]
func (n *NotebookHandler) Create(c *gin.Context) error {
	notebook := &entity.Notebook{}
	err := c.ShouldBindBodyWith(notebook, binding.JSON)
	if err != nil {
		log.Errorf("params invalid: %v", err)
		return err
	}
	notebook.UserID = c.Param("userID")
	//默认给一个项目id
	notebook.ProjectID = c.Param("userID") + "_" + uuid.NewUUID().String()
	//userInfo, err := getUserInfo(c)
	//if err != nil {
	//	return entity.NewError(entity.CodeParamInvalid, entity.MsgGetUserInfoError)
	//}

	if err = validate.ValidateCreateNotebook(n.resourceManager, notebook); err != nil {
		return err
	}
	newNotebook, err := n.resourceManager.CreateNotebook(notebook)
	if err != nil {
		return err
	}
	success(c, newNotebook)
	return nil
}

// 获取notebook列表
// @Summary 获取notebook列表
// @Description 获取notebook列表
// @Tags code studio管理
// @Accept  json
// @Produce  json
// @Param userID path    string true "default"
// @Param pageSize query int true "default 0"
// @Param pageNum  query int true "default 0"
// @Param keyword  query string true "default "
// @Success 200 {object} entity.HttpRespond{data=entity.HttpRespondList} "按时间倒序排序"
// @Failure 200 {object} entity.HttpRespond
// @Router /users/{userID}/notebooks [get]
func (n *NotebookHandler) List(c *gin.Context) error {
	option := &entity.ProjectListOption{}
	err := c.ShouldBindQuery(option)
	if err != nil {
		return entity.NewCodeError(entity.CodeParamInvalid)
	}
	option.UserID = c.Param("userID")
	total, list, err := n.resourceManager.ListNotebook(option)
	if err != nil {
		return entity.NewCodeError(entity.CodeSystemError)
	}
	success(c, entity.HttpRespondList{
		List:  list,
		Total: total,
	})
	return nil
}

// 删除notebook
// @Summary 删除notebook
// @Description 删除notebook
// @Tags code studio管理
// @Accept  json
// @Produce  json
// @Param notebookID path    string true "default"
// @Success 200 {object} entity.HttpRespond{}
// @Failure 200 {object} entity.HttpRespond
// @Router /notebooks/{notebookID} [delete]
func (n *NotebookHandler) Delete(c *gin.Context) error {
	paramID := &ParamID{}
	err := c.ShouldBindUri(paramID)
	if err != nil {
		return entity.NewError(entity.CodeParamInvalid, entity.MsgParamIDInvalid)
	}

	log.Infof("delete notebook, id: %v", paramID.ID)
	err = n.resourceManager.DeleteNotebook(paramID.ID)
	if err != nil {
		return err
	}

	success(c, nil)
	return nil
}

// 删除user notebook records
// @Summary 删除user notebook records(微服务间调用接口)
// @Description 删除user notebook records(微服务间调用接口)
// @Tags code studio管理
// @Accept  json
// @Produce  json
// @Param userID path    string true "default"
// @Success 200 {object} entity.HttpRespond{}
// @Failure 200 {object} entity.HttpRespond
// @Router /users/:userID/notebooks [delete]
func (n *NotebookHandler) DeleteRecords(c *gin.Context) error {
	userId := c.Param("userID")
	log.Infof("delete user notebook and pvc records, userId: %v", userId)
	err := n.resourceManager.DeleteUserRecords(userId)
	if err != nil {
		return err
	}

	success(c, "删除user notebook and pvc records成功")
	return nil
}

// 删除项目时删除项目下的notebook
// @Tags code studio管理
// @Accept  json
// @Produce  json
// @Param userID path    string true "default"
// @Success 200 {object} entity.HttpRespond{}
// @Failure 200 {object} entity.HttpRespond
// @Router /users/:userID/notebooks [delete]
func (n *NotebookHandler) DeleteByProjectId(c *gin.Context) error {
	projectId := c.Param("projectId")
	log.Infof("delete project notebook and pvc records, projectId: %v", projectId)
	err := n.resourceManager.DeleteNotebookByProjectId(projectId)
	if err != nil {
		log.Errorf("delete error: %v", err)
		return err
	}

	success(c, "删除项目下的notebook成功")
	return nil
}

// 编辑notebook
// @Summary 编辑notebook
// @Description 编辑notebook
// @Tags code studio管理
// @Accept  json
// @Produce  json
// @Param userID path    string true "default"
// @Param ModifyNotebook body entity.ModifyNotebook true "test"
// @Success 200 {object} entity.HttpRespond{data=entity.Notebook}
// @Failure 200 {object} entity.HttpRespond
// @Router /notebooks/{notebookID} [patch]
func (n *NotebookHandler) Modify(c *gin.Context) error {
	modifyNotebook := &entity.ModifyNotebook{}
	err := c.ShouldBindBodyWith(modifyNotebook, binding.JSON)
	if err != nil {
		log.Errorf("params invalid: %v", err)
		return err
	}
	paramID := &ParamID{}
	err = c.ShouldBindUri(paramID)
	if err != nil {
		return entity.NewError(entity.CodeParamInvalid, entity.MsgParamIDInvalid)
	}

	if err = validate.ValidateModifyNotebook(n.resourceManager, modifyNotebook); err != nil {
		return err
	}

	newNotebook, err := n.resourceManager.ModifyNotebook(paramID.ID, modifyNotebook)
	if err != nil {
		return err
	}
	success(c, newNotebook)
	return nil
}

// 启动notebook
// @Summary 启动notebook
// @Description 启动notebook
// @Tags code studio管理
// @Accept  json
// @Produce  json
// @Param notebookID path    string true "default"
// @Success 200 {object} entity.HttpRespond{}
// @Failure 200 {object} entity.HttpRespond
// @Router /notebooks/{notebookID}/start [patch]
func (n *NotebookHandler) Start(c *gin.Context) error {
	paramID := &ParamID{}
	err := c.ShouldBindUri(paramID)
	if err != nil {
		return entity.NewError(entity.CodeParamInvalid, entity.MsgParamIDInvalid)
	}

	log.Infof("start notebook, id: %v", paramID.ID)
	err = n.resourceManager.StartNotebook(paramID.ID)
	if err != nil {
		return err
	}

	success(c, nil)

	return err
}

// 停止notebook
// @Summary 停止notebook
// @Description 停止notebook
// @Tags code studio管理
// @Accept  json
// @Produce  json
// @Param notebookID path    string true "default"
// @Success 200 {object} entity.HttpRespond{}
// @Failure 200 {object} entity.HttpRespond
// @Router /notebooks/{notebookID}/stop [patch]
func (n *NotebookHandler) Stop(c *gin.Context) error {
	paramID := &ParamID{}
	err := c.ShouldBindUri(paramID)
	if err != nil {
		return entity.NewError(entity.CodeParamInvalid, entity.MsgParamIDInvalid)
	}

	log.Infof("Stop notebook, id: %v", paramID.ID)
	err = n.resourceManager.StopNotebook(paramID.ID)
	if err != nil {
		return err
	}

	success(c, nil)
	return err
}

// 记录notebook活跃
// @Summary 记录notebook活跃
// @Description 记录notebook活跃
// @Tags code studio管理
// @Accept  json
// @Produce  json
// @Param notebookID path    string true "default"
// @Success 200 {object} entity.HttpRespond{}
// @Failure 200 {object} entity.HttpRespond
// @Router /notebooks/{notebookID}/active [patch]
func (n *NotebookHandler) Active(c *gin.Context) error {
	paramID := &ParamID{}
	err := c.ShouldBindUri(paramID)
	if err != nil {
		return entity.NewError(entity.CodeParamInvalid, entity.MsgParamIDInvalid)
	}

	select {
	case n.activeIDChan <- paramID.ID:
	default:
		go func() { n.activeIDChan <- paramID.ID }()
	}
	success(c, nil)
	return nil
}

// 查看notebook日志
// @Summary 查看notebook日志
// @Description 查看notebook日志
// @Tags code studio管理
// @Accept  json
// @Produce  json
// @Param notebookID path string true "default"
// @Success 200 {object} entity.HttpRespond{}
// @Failure 200 {object} entity.HttpRespond
// @Router /notebooks/{notebookID}/logs [get]
func (n *NotebookHandler) Logs(c *gin.Context) error {
	paramId := &ParamID{}
	err := c.ShouldBindUri(paramId)
	if err != nil {
		return entity.NewError(entity.CodeParamInvalid, entity.MsgParamIDInvalid)
	}
	log.Infof("Get notebook %v logs", paramId.ID)
	notebookLog, err := n.resourceManager.GetNotebookLogs(paramId.ID)
	if err != nil {
		return err
	}
	success(c, notebookLog)
	return nil
}

// admin
func (n *NotebookHandler) ListAll(c *gin.Context) error {
	success(c, "ok")
	return nil
}
